package service

import (
	"crow-qin/app/config"
	"crow-qin/app/pkg"
	"errors"
	"fmt"
	"github.com/dgrijalva/jwt-go"
	"github.com/gin-gonic/gin"
	"github.com/toolkits/pkg/logger"
	"time"
)

// JWTClaims
// token里面添加用户信息，验证token后可能会用到用户信息
type JWTClaims struct {
	jwt.StandardClaims
	UserID      int      `json:"user_id"`
	Password    string   `json:"password"`
	Username    string   `json:"username"`
	FullName    string   `json:"full_name"`
	Permissions []string `json:"permissions"`
}

const (
	tokenKeyPrefix = "crow:token:"
	tokenTimeOut   = 3600 * 24
)

var (
	Secret     = "LiuBei"  // 加盐
	ExpireTime = 3600 * 24 // token有效期
)

// Set
// 设置JWTClaim的值
func (j *JWTClaims) Set(userId int64, userName string, password string, FullName string) {
	j.UserID = int(userId)
	j.Username = userName
	j.Password = password
	j.FullName = userName
	j.Permissions = []string{}
	j.IssuedAt = time.Now().Unix()
	j.ExpiresAt = time.Now().Add(time.Second * time.Duration(ExpireTime)).Unix()
	logger.Infof("%+v", j)
}

// CreateToken
// 创建token
func (j *JWTClaims) CreateToken() (crowToken string, err error) {
	//创建token
	token := jwt.NewWithClaims(jwt.SigningMethodHS256, j)
	crowToken, err = token.SignedString([]byte(Secret))
	if err != nil {
		logger.Error(err)
		return crowToken, err
	}
	logger.Info(crowToken)

	//存入redis
	redisKey := fmt.Sprintf("%s%s", tokenKeyPrefix, crowToken)
	err = config.Rdb.Set(config.RedisCtx, redisKey, j.Username, time.Second*tokenTimeOut).Err()
	if err != nil {
		logger.Info(err)
	}
	return crowToken, nil
}

// Verify
// 从上下文获取token并验证
func (j *JWTClaims) Verify(c *gin.Context) (result bool, userName string, err error) {

	//从C中获取token
	crowToken, err := pkg.GetTokenFromC(c)
	if err != nil {
		result = false
		return result, "", nil
	}

	//验证token，并获取用户名
	err = j.verifyAction(crowToken)
	if err != nil {
		result = false
		return result, "", nil
	} else {
		logger.Infof("jwtClaims : %+v", j)
		result = true
	}

	userName = j.Username

	//对比redis中的token
	redisValue := config.Rdb.Get(config.RedisCtx, tokenKeyPrefix+crowToken)
	if redisValue.Val() != j.Username {
		err = errors.New("用户token验证失败：002")
		return false, userName, err
	}
	//logger.Infof("redis中的用户名：%s，token的用户名：%s", redisValue.Val(), j.Username)
	return result, userName, nil
}

// verifyAction
// 解析token，获取用户信息
func (j *JWTClaims) verifyAction(crowToken string) (err error) {

	token, err := jwt.ParseWithClaims(crowToken, &JWTClaims{}, func(token *jwt.Token) (interface{}, error) {
		return []byte(Secret), nil
	})
	if err != nil {
		return err
	}

	m, ok := token.Claims.(*JWTClaims)
	if !ok {
		return err
	}
	err = token.Claims.Valid()
	if err != nil {
		return err
	}
	j.StandardClaims = m.StandardClaims
	j.Username = m.Username
	j.FullName = m.FullName
	j.UserID = m.UserID
	return nil
}

// RefreshToken
// 刷新token
func (j *JWTClaims) RefreshToken(strToken string) (newToken string, err error) {

	//从token获取用户信息
	//claims, err := verifyAction(strToken)
	err = j.verifyAction(strToken)
	if err != nil {
		logger.Error(err)
		return newToken, err
	}

	j.ExpiresAt = time.Now().Unix() + (j.ExpiresAt - j.IssuedAt)

	//生成新token
	newToken, err = j.CreateToken()
	if err != nil {
		logger.Error(err)
		return newToken, err
	}

	//存入redis
	redisKey := fmt.Sprintf("%s%s", tokenKeyPrefix, newToken)
	err = config.Rdb.Set(config.RedisCtx, redisKey, j.Username, time.Second*tokenTimeOut).Err()
	if err != nil {
		logger.Info(err)
	}

	//删除旧token
	err = config.Rdb.Del(config.RedisCtx, tokenKeyPrefix+strToken).Err()
	if err != nil {
		logger.Error(err)
	}
	return newToken, nil
}

// Logout
// 从redis中删除token
func Logout(strToken string) error {
	//删除redis中的token
	err := config.Rdb.Del(config.RedisCtx, tokenKeyPrefix+strToken).Err()
	if err != nil {
		logger.Error(err)
		return err
	}
	return nil
}
